/*
 * gen.c
 *
 * Implements code generation.
 */


#include <stdio.h>
#include "lir.h"
#include "ir_map_pascal.h"
#include "lir_map_ir.h"
#include "lir_print.h"
#include "gen.h"
#include "gen_backend_asm.h"
#include "lir_map_ir.h"
#include "symtab.h"
#include "mem.h"


/* forward declarations */

/* File descriptor used for the output of the code generation phase */
//FILE *gen_fd;
/* Flag used to distinguish which, of the many, code generation
   backends should be used. */
int gen_backend_type;



extern struct args_t cmdArgs;



/* Generates code */
void gen(struct ir_list_t *ir_list)
{
  struct lir_t *lir;
  
  /* Should be retrieving the output filename from the command-line
     really */
#define GEN_FILE "out.asm"
  /* Create an output stream in which we will print the generated code */
  gen_fd = fopen(GEN_FILE, "w");
  if (gen_fd == NULL) {
    printf("Could not open file %s for code output\n", GEN_FILE);
    perror("Error was");
    exit(-1);
  }
#undef GEN_FILE

  init_tmp_variables();
  // Initialize the global lir list for use
  global_lir_list = global_lir_list_init();

  struct ir_list_t *temp = global_ir_list;

  /* adding initial data */
  gen_backend_asm_init();

  while (temp != NULL) {
	  struct ir_t *ir = temp->ir;

	  /* Generate a lower-level IR */
	  lir = lir_map_ir_create(ir);

	  if (cmdArgs.printLir == 1)
		  lir_print(lir);

	  /* Generate code with the ASM backend */
	  gen_backend_asm(lir);

	  temp = temp->next;
  }

  gen_backend_asm_deinit();

  // Put the printing statement at the very end of the code
  fprintf(gen_fd, "\n\n.data\nnewline: .asciiz \"\\n\" \n.text\n");
  /* Close the code generation output stream */
  fclose(gen_fd);
}



/* Initializes the code generation backend. This must happen before
   parsing even begins. The reason is that we want to set in the table
   of datatypes the sizes of the primitive types for the "backend
   architecture" we will produce code later on. */
void gen_init()
{
	/* Should be retrieving the backend type from the command-line or
	   from default configuration options from a file. */
	gen_backend_type = GEN_BACKEND_TYPE_MIPS_ASM;
}



/* Outputs string to the output stream used in the code generation
   phase */
void gen_write(char *string)
{
	unsigned long size;
	unsigned long count;

	size = strlen(string);

	count = fwrite(string, size, 1, gen_fd);
	if (count != 1) {
		printf("Writing to the code generation output stream returned count of %ld instead of %ld\n", count, size);
		perror("Error was");
		exit(-1);
	}
}



/* Returns the size of a primitive type for the current backend */
long gen_primitive_size(char *primitive_type)
{
	long size;

	if (gen_backend_type == GEN_BACKEND_TYPE_MIPS_ASM)
		size = gen_backend_asm_primitive_size(primitive_type);

	if (size == -1) {
		char *e; 

		e = (char *) malloc(MAX_ERROR_SIZE);
		CHECK_MEM_ERROR(e)

			error_check_not_null(&primitive_type);

		sprintf(e, "Internal compiler error. Primitive type \"%s\" is not supported for backend %d", primitive_type, gen_backend_type);
		error(-1, e);
	}

	return size;
}



/* Iterates over the symbol table and computes the offsets of all
   variables as will be layed out in memory */
void gen_compute_offsets()
{
	/* It would have been possible to try and inspect the parse tree
	   again to find the main class. But this would be the WRONG place
	   to do it. Remember that our compiler is the greatest thing since
	   sliced bread, and supports frontends for myriads of other
	   languages, whose parse trees could be different.

	   After transforming from the frontend to the IR, we should never
	   touch the parse tree again.
	   */

	/* First compute the sizes of all the datatypes */
	usrdef_compute_sizes();

	/* Now compute offsets for the variables of all classes */
	symtab_compute_offsets();
}
